#include "hierarchical_menu_button.h"
#include <QEvent>

namespace
{

unsigned get_menu_flags(QMenu *menu) // for menus which items contain flags (bitmask), returned value is bitmask
{
    unsigned state = 0;
    foreach(QAction *act, menu->actions())
    {
        QVariant data = act->data();
        if(data.isValid() && act->isChecked())
        {
            state |= data.toUInt();
        }
    }
    return state;
}

hierarchical_menu_button::menu_state_list_t get_menu_state(QMenu *menu)
{
    hierarchical_menu_button::menu_state_list_t list;

    for(auto act : menu->actions())
        list.append(hierarchical_menu_button::menu_state_t(act->data().toString(), act->isChecked()));

    return list;
}

} // end of anonymous namespace



hierarchical_menu_button::hierarchical_menu_button(const checkable_menu::tree &data, QWidget *parent)
    : QPushButton (parent)
    , main_menu_  (new checkable_menu::menu(this))
{
    init(data);
}

hierarchical_menu_button::hierarchical_menu_button(const checkable_menu::tree &data, const QString &button_text, QWidget *parent)
    : QPushButton (button_text, parent)
    , main_menu_  (new checkable_menu::menu(this))
{
    init(data);
}

void hierarchical_menu_button::add_menu(const QString &parent_key, const checkable_menu::tree &data)
{
    if(!action_hash_.contains(parent_key)) {
        qWarning() << "hierarchical_menu_button::add_menu > no such menu key:" << parent_key;
        return;
    }

    auto act = action_hash_[parent_key];

    QList<QWidget*> widgets = act->associatedWidgets();
    if (!widgets.isEmpty()) {
        if(act->menu() == nullptr) {
            if (QMenu *parent_menu = qobject_cast<QMenu*>(widgets[0])) {
                auto m = new checkable_menu::menu(act->text(), parent_menu);
                m->setObjectName("menu" + act->text());
                act->setMenu(m);

                add_menu(data, data.get_root_node_children(), m);
            }
        }
        else {
            if(auto m = qobject_cast<checkable_menu::menu*>(act->menu()))
                add_menu(data, data.get_root_node_children(), m);
        }
    } else {
        qWarning() << "hierarchical_menu_button::add_menu > action" << act->objectName() << "don't belong to any widget";
        return;
    }
}

void hierarchical_menu_button::set_checked(const QString &key, bool checked)
{
    if(!action_hash_.contains(key))
    {
        qWarning() << "hierarchical_menu_button::set_checked > no such menu key:" << key;
        return;
    }

    if(auto m = qobject_cast<checkable_menu::menu*>(action_hash_[key]->menu()))
    {
        m->set_checked(checked);
    }
    else
    {
        auto act = action_hash_[key];
        act->setChecked(checked);

        QList<QWidget *> widgets = act->associatedWidgets();
        if (!widgets.isEmpty()) {
            if (QMenu *menu = qobject_cast<QMenu *>(widgets[0])) {
                checkable_menu::set_parent_checked(menu, checkable_menu::is_all_checked(menu->actions()));
            }
        }
    }
}

void hierarchical_menu_button::set_enabled(const QString &key, bool enabled)
{
    if(!action_hash_.contains(key))
    {
        qWarning() << "hierarchical_menu_button::set_enabled > no such menu key:" << key;
        return;
    }
    action_hash_[key]->setEnabled(enabled);
}

hierarchical_menu_button::menu_state_list_t hierarchical_menu_button::get_menu_state(const QString &key) const
{
    if(!action_hash_.contains(key)) {
        qWarning() << "hierarchical_menu_button::menu_state > no such menu key:" << key;
        return menu_state_list_t();
    }

    return ::get_menu_state(action_hash_[key]->menu());
}

hierarchical_menu_button::menu_state_list_t hierarchical_menu_button::get_main_menu_state() const
{
    return ::get_menu_state(main_menu_);
}

bool hierarchical_menu_button::has_key(const QString &key) const
{
    return action_hash_.contains(key);
}

void hierarchical_menu_button::on_menu_hide()
{
//    qDebug() << "hierarchical_menu_button::on_menu_hide";
//    for(auto it : get_menu_state("key_points")) {
//        qDebug() << it.first << it.second;
//    }

//    for(auto it : get_main_menu_state()) {
//        qDebug() << it.first << it.second;
//    }

    setChecked(false);
}

void hierarchical_menu_button::on_check_changed(QAction *act)
{
//    qDebug() << "hierarchical_menu_button::on_check_changed";
    const auto data = act->data().value<menu_data_t>();
//    qDebug() << std::get<dk_key_string>(data) << std::get<dk_type>(data) <<  std::get<dk_value>(data).toInt() << act->isChecked() << "checkstate changed";
    emit check_state_changed(std::get<dk_key_string>(data), std::get<dk_type>(data), std::get<dk_value>(data), act->isChecked());
}

bool hierarchical_menu_button::eventFilter(QObject *obj, QEvent *e)
{
    if (e->type() == QEvent::Show && obj == menu())
    {
        menu()->move(mapToGlobal(QPoint(0, 0)) + QPoint(width(), 0));
        return true;
    }
    return QPushButton::eventFilter(obj, e);
}

//void hierarchical_menu_button::mousePressEvent(QMouseEvent * /*e*/)
//{
//}

//void hierarchical_menu_button::enterEvent(QEvent *e)
//{
//    setChecked(true);
//    main_menu_->popup(mapToGlobal(QPoint(0, 0)) + QPoint(width() - 3, 0));
//    QPushButton::enterEvent(e);
//}

//void hierarchical_menu_button::leaveEvent(QEvent *e)
//{
//    QPushButton::leaveEvent(e);
//}

void hierarchical_menu_button::add_menu(const checkable_menu::tree &data, const std::vector<QString> &children, checkable_menu::menu *menu)
{
    for(auto it : children)
    {
        if(action_hash_.contains(it)) {
            qWarning() << "hierarchical_menu_button::add_menu > menu with key" << it << "already exists";
            continue;
        }

        const auto node = data.get_node(it);

        if(!node.empty())
        {
            if(node.children.empty())
            {
                QAction *act = menu->addAction(node.text);
                act->setObjectName("action of menu" + node.key);
                act->setCheckable(true);
                act->setData(QVariant::fromValue(std::make_tuple(node.key, node.type, node.value)));

                if(menu->menuAction()->isChecked())
                    act->setChecked(true);

                action_hash_.insert(node.key, act);
            }
            else
            {
                auto m = new checkable_menu::menu(node.text, menu);
                m->setObjectName(node.key);
                m->menuAction()->setObjectName("menuAction of" + node.key);
                m->menuAction()->setCheckable(true);
                m->menuAction()->setData(QVariant::fromValue(std::make_tuple(node.key, node.type, node.value)));

                add_menu(data, node.children, m);

                menu->addMenu(m);
                action_hash_.insert(node.key, m->menuAction());
            }
        }
    }
}

void hierarchical_menu_button::init(const checkable_menu::tree &data)
{
    setCheckable(true);
    setMenu(main_menu_);
    main_menu_->installEventFilter(this);
    main_menu_->setObjectName("main_menu");

    connect(main_menu_, SIGNAL(aboutToHide()), this, SLOT(on_menu_hide()));

    add_menu(data, data.get_root_node_children(), main_menu_);

    connect(main_menu_, SIGNAL(triggered(QAction*)), this, SLOT(on_check_changed(QAction*)));
}


